Skip to content

Conversation

@phortx
Copy link

@phortx phortx commented Sep 17, 2025

... also some smaller formatting fixes that the IDE did.

See #172

@coddingtonbear coddingtonbear added the needs more discussion More information or discussion is necessary before proceeding label Sep 28, 2025
@phortx
Copy link
Author

phortx commented Oct 30, 2025

Hi @coddingtonbear,

what do you think? Could we merge this by time? Do you need anything from my side? :)

Comment on lines 1029 to 1052
if (match[0] == 0) {
// When start position is between 0 and positionOffset, that means the search term matched within the headline.
contextMatches.push({
match: {
start: match[0],
end: match[1],
source: "filename"
},
context: file.basename,
});
} else {
// Otherwise, the match was in the content
contextMatches.push({
match: {
start: match[0] - positionOffset,
end: match[1] - positionOffset,
source: "content"
},
context: cachedContents.slice(
Math.max(match[0] - contextLength, positionOffset),
match[1] + contextLength
),
});
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a lot of possibility for off-by-one types of errors and other kinds of bugs in this kind of logic; could I trouble you to writing some tests covering this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I really appreciate your in-depth review. I will add more tests in the next days. Have a great sunday!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: I fixed all copilot comments in the MR. It found a bug, which is nice!

Also I added lot's of test cases. Tested: works like a charm:

$ curl -X 'POST' \
      'http://127.0.0.1:27123/search/simple/?query=Master%20Plan' \
      -H 'accept: application/json' \
      -H 'Authorization: Bearer 320fac56a929e907fd1f724b12eb55327b38a363e8c8a8cbc290c9c5f1c30631'
      
[
  [...],
  {
    "filename": "5 - Finanzen/1 - Master Plan.md",
    "score": -1.2066,
    "matches": [
      {
        "match": {
          "start": 4,
          "end": 10,
          "source": "filename"
        },
        "context": "1 - Master Plan"
      },
      {
        "match": {
          "start": 11,
          "end": 15,
          "source": "filename"
        },
        "context": "1 - Master Plan"
      }
    ]
  }
]

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR enhances the search functionality to include file names in the search scope and distinguish between filename and content matches. The implementation prepends file basenames to the search text and handles match position offsets accordingly.

  • Added a source field to SearchContext to distinguish between filename and content matches
  • Modified search logic to prepend file basename to searchable text and adjust match positions
  • Fixed several formatting issues (whitespace, spacing) in the mock files

Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
src/types.ts Added source field to SearchContext interface to distinguish match origin
src/requestHandler.ts Enhanced search to include filenames and differentiate match sources; fixed contextLength parsing bug
mocks/obsidian.ts Fixed spacing/formatting inconsistencies; added basename property to TFile mock
docs/openapi.yaml Updated API documentation with descriptions and new source field

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

phortx and others added 3 commits November 6, 2025 09:36
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
- Changed condition from match[0] === 0 to match[0] < positionOffset
  to correctly detect all matches within the filename, not just at position 0
- Added comprehensive test suite for searchSimplePost with 12 test cases
  covering filename matches, content matches, edge cases, and position calculations
- Implemented functional prepareSimpleSearch mock for testing

Fixes issue where files like '1 - Master Plan.md' were not found
when searching for 'Master Plan' because the match didn't start at position 0.

Addresses PR feedback:
- Tests now cover off-by-one errors and boundary conditions
- Match detection logic correctly identifies all filename matches
- Position calculations verified through tests
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1029 to +1038
if (match[0] < positionOffset) {
// When start position is between 0 and positionOffset, that means the search term matched within the filename.
contextMatches.push({
match: {
start: match[0],
end: match[1],
source: "filename"
},
context: file.basename,
});
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition match[0] < positionOffset doesn't handle matches that span across the filename boundary into the newlines or content. If a match starts in the filename but extends beyond it (e.g., searching for "filename\n"), the match end position could exceed the length of file.basename (the context), which may confuse API consumers. Consider checking match[1] <= positionOffset to ensure the entire match is within the filename, or clamp the end position to the basename length.

Copilot uses AI. Check for mistakes.
Comment on lines +1048 to +1049
Math.max(match[0] - contextLength, positionOffset),
match[1] + contextLength
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The context slicing has incorrect position calculations. Since match[0] and match[1] are positions in the combined text (filename + content), but we're slicing from cachedContents, the positions need to be adjusted. The slice should be:

cachedContents.slice(
  Math.max(match[0] - positionOffset - contextLength, 0),
  match[1] - positionOffset + contextLength
)

Currently, the code uses unadjusted positions match[0] and match[1] which will extract the wrong portion of the content.

Suggested change
Math.max(match[0] - contextLength, positionOffset),
match[1] + contextLength
Math.max(match[0] - positionOffset - contextLength, 0),
match[1] - positionOffset + contextLength

Copilot uses AI. Check for mistakes.
Comment on lines +1020 to +1022
// We added the headline to the search text with 2 line breaks.
// That causes the start and end position numbers to be wrong with an offset
// of the char length of the headline line breaks.
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The comment mentions "2 line breaks" but technically uses 2 newline characters (\n\n). While this is a minor point, consider updating to "2 newline characters" or "a double newline" for technical accuracy.

Suggested change
// We added the headline to the search text with 2 line breaks.
// That causes the start and end position numbers to be wrong with an offset
// of the char length of the headline line breaks.
// We added the headline to the search text with 2 newline characters.
// That causes the start and end position numbers to be wrong with an offset
// of the char length of the headline newline characters.

Copilot uses AI. Check for mistakes.
Comment on lines +1016 to +1022
// Add the headline to the search text to include it in the search.
const filenamePrefix = file.basename + "\n\n";
const result = search(filenamePrefix + cachedContents);

// We added the headline to the search text with 2 line breaks.
// That causes the start and end position numbers to be wrong with an offset
// of the char length of the headline line breaks.
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The comment refers to "headline" but the code is actually adding the filename/basename. Consider using "filename" instead of "headline" for clarity and consistency with the rest of the code.

Suggested change
// Add the headline to the search text to include it in the search.
const filenamePrefix = file.basename + "\n\n";
const result = search(filenamePrefix + cachedContents);
// We added the headline to the search text with 2 line breaks.
// That causes the start and end position numbers to be wrong with an offset
// of the char length of the headline line breaks.
// Add the filename to the search text to include it in the search.
const filenamePrefix = file.basename + "\n\n";
const result = search(filenamePrefix + cachedContents);
// We added the filename to the search text with 2 line breaks.
// That causes the start and end position numbers to be wrong with an offset
// of the char length of the filename line breaks.

Copilot uses AI. Check for mistakes.
async writeBinary(path: string, content: ArrayBuffer, option?:DataWriteOptions): Promise<void> {
this._writeBinary = [path,content]
async writeBinary(path: string, content: ArrayBuffer, option?: DataWriteOptions): Promise<void> {
this._writeBinary = [path, content]
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing semicolon at the end of the statement. For consistency with the rest of the codebase, add a semicolon after content].

Suggested change
this._writeBinary = [path, content]
this._writeBinary = [path, content];

Copilot uses AI. Check for mistakes.
Copy link
Owner

@coddingtonbear coddingtonbear left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a couple more comments -- once you've updated the mocks and (I assume) your tests, let me know!

query: string
): (value: string) => null | SearchResult {
return null;
return (text: string) => {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a quick note about this file, @phortx: since it lives under mocks/, it’s meant purely as a mock—basically something lightweight so the type checker and tests have a stand-in. We shouldn’t put any real implementation logic here.

Instead, the idea is that your tests can control what this mock returns in different scenarios. If you take a peek at some of the other mock functions in mocks/obsidian.ts, you’ll see how they’re set up to let tests override behavior as needed.

So to make this consistent: this function shouldn’t implement any real behavior. It should either do nothing or return whatever value your test explicitly configures, so that you can verify how requestHandler.ts responds to those different return values.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs more discussion More information or discussion is necessary before proceeding

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants